package com.icesoft.core.dao.criteria;

import java.util.ArrayList;
import java.util.List;

public class WhereBuilder extends BaseBuilder {

    private List<CriteriaPropertyFilter> propertyFilters = new ArrayList<>(10);
    private AndPropertyFilter propertyFilter;
    PropertyFilter currentFilter;
    int propertySeq = 0;

    WhereBuilder copy(FromBuilder fromBuilder) {
        WhereBuilder whereBuilder = new WhereBuilder(fromBuilder);
        whereBuilder.currentFilter = currentFilter.copy(fromBuilder);
        whereBuilder.propertyFilters.addAll(propertyFilters);
        whereBuilder.propertySeq = propertySeq;
        if (propertyFilter != null) {
            whereBuilder.propertyFilter = propertyFilter.copy(fromBuilder);
        }
        return whereBuilder;
    }

    WhereBuilder(FromBuilder fromBuilder) {
        super(fromBuilder);
        fromBuilder.whereBuilder = this;
        currentFilter = andFilter();
    }

    public AndPropertyFilter filter() {
        if (propertyFilter == null) {
            propertyFilter = new PropertyFilter(fromBuilder);
        }
        return propertyFilter;
    }

    public PropertyFilter orFilter() {
        currentFilter = new PropertyFilter(fromBuilder);
        propertyFilters.add(new CriteriaPropertyFilter(currentFilter, Criteria.OR));
        return currentFilter;
    }

    public PropertyFilter andFilter() {
        currentFilter = new PropertyFilter(fromBuilder);
        propertyFilters.add(new CriteriaPropertyFilter(currentFilter, Criteria.AND));
        return currentFilter;
    }

    @Override
    public String toString() {
        return toString(true);
    }

    public String getHql() {
        return toString(false);
    }

    private String toString(boolean isValue) {
        boolean isEmpty = true;
        StringBuilder stringBuilder = new StringBuilder();
        long filterSize = propertyFilters.stream().filter(p -> p.propertyFilter.propertys.size() > 0).count();
        for (CriteriaPropertyFilter queryBuilder : propertyFilters) {
            int propertySize = queryBuilder.propertyFilter.propertys.size();
            if (propertySize == 0) {
                continue;
            }
            if (!isEmpty) {
                stringBuilder.append(' ').append(queryBuilder.criteria).append(' ');
            }
            isEmpty = false;
            if (propertySize > 1 && filterSize > 1) {
                stringBuilder.append('(');
            }
            if (isValue) {
                stringBuilder.append(queryBuilder.propertyFilter.toString());
            } else {
                stringBuilder.append(queryBuilder.propertyFilter.getHql());
            }
            if (propertySize > 1 && filterSize > 1) {
                stringBuilder.append(')');
            }
        }
        if (isFilterEnable()) {
            if (!isEmpty) {
                stringBuilder.insert(0, '(').append(')');
                stringBuilder.append(' ').append(Criteria.AND).append(' ');
            }
            if (isValue) {
                stringBuilder.append(propertyFilter.toString());
            } else {
                stringBuilder.append(propertyFilter.getHql());
            }
        }
        if (stringBuilder.length() == 0) {
            return "";
        }
        return "where " + stringBuilder.toString();
    }

    private boolean isFilterEnable() {
        return propertyFilter != null && !propertyFilter.propertys.isEmpty();
    }

    public List<PropertyFilter.Property> getPropertys() {
        List<PropertyFilter.Property> list = new ArrayList<>();
        propertyFilters.forEach(pf -> pf.propertyFilter.propertys.forEach(p -> {
            if (!p.editOperator) {
                list.add(p);
            }
        }));
        if (isFilterEnable()) {
            propertyFilter.propertys.forEach(p -> {
                if (!p.editOperator) {
                    list.add(p);
                }
            });
        }
        return list;
    }

    static class CriteriaPropertyFilter {
        PropertyFilter propertyFilter;
        Criteria criteria;

        public CriteriaPropertyFilter(PropertyFilter propertyFilter, Criteria criteria) {
            super();
            this.propertyFilter = propertyFilter;
            this.criteria = criteria;
        }

    }
}
